/*
* ============================================================================
*  Name        : platform_qt.h
*  Part of     : Finite state machine utilities
*  Description : Platform abstruction layer
*  Version     : %version: %
*  
*
* ============================================================================
*/
/**
 * @file sea_platform_qt.h
 *
 * @ingroup Platforms
 */

#if !defined( __sea_platform_qt__ )
#define __sea_platform_qt__

#include <string>
#include <vector>
#include <map>
#include <algorithm>
#include <typeinfo>
#include <stdlib.h>
#include <assert.h>
#include <time.h>
#include <conio.h>
#include <iostream>

#include <QApplication>
#include <QMetaObject>
#include <QDebug>
#include <QtCore>
#include <QString>
#include <QUuid>
#include <QThread>
#include <QSemaphore>
#include <QMutex>
#include <QTimer>
#include <QWaitCondition>
#include <QHash>
#include <QTime>
#include <QDateTime>

#include "sea_assert.h"

using namespace std;

namespace sea
{
    #define SEA_PRETTY_FUNCTION Q_FUNC_INFO

    typedef QUuid Guid;
    typedef string String;
    typedef QTime OsTime;
    typedef Qt::HANDLE OsThreadHandle;

    /**
     * @class OsThread
     * @brief Thread in Qt
     *
     * @ingroup Platforms
     */
    class OsThread  : virtual public QThread , public Object
    {
    public:

        OsThread( ) : m_started( false ), m_threadId( 0 ) { }

        void start( ) 
        { 
            if ( !m_started )
            {
                m_started = true;
                QThread::start( );
            }
        }

        virtual void exec( ) { }

        void run( )
        {
            m_threadId = currentThreadId( );
            exec( );
        }

        OsThreadHandle id( ) { return m_threadId; }
        static OsThreadHandle currentThreadId( ){ return QThread::currentThreadId( ); }

        void setPriority( OsThreadPriority value )
        {
            if ( value >= Lowest && value <= Highest )
            {
                QThread::setPriority( m_native_priority[ value ] );
            }
        }
    private:
        static QThread::Priority m_native_priority[ ];
        bool m_started;
        OsThreadHandle m_threadId;
    };

    /**
     * @class OsSemaphore
     * @brief Semaphore in Qt
     *
     * @ingroup Platforms
     */
    class OsSemaphore : virtual public QSemaphore, public Object
    {
    public:
        OsSemaphore( int init_counter = 0 ) :
          QSemaphore( init_counter ) { }
        
        virtual void signal( int number = 1 ) { release( number ); }
        virtual void wait( int number = 1 ) { acquire( number ); }
    };

    /**
     * @class OsMutex
     * @brief Mutex in Qt
     *
     * @ingroup Platforms
     */
    class OsMutex: virtual public QMutex, public Object
    {
    public:
        OsMutex( int recursive = 1 ): 
            QMutex( static_cast< QMutex::RecursionMode >( recursive ) )
        { }
        bool try_enter( ){ return tryLock( ); }
        void enter( ){ QMutex::lock( ); }
        void leave( ){ QMutex::unlock( ); }

        /**
         * @class Scoped
         * @brief Mutex scoped lock/unlock
         *
         * @ingroup Platforms
         */
        class Scoped
        {
        public:
            Scoped( OsMutex* critical_section, bool do_try = false );
            ~Scoped( );
        private:
            OsMutex* m_mutex;
        };
    };

    /**
     * @class OsSpinlock
     * @brief Spinlock in Windows
     *
     * @ingroup Platforms
     */
    class OsSpinlock
    {
    public:
        OsSpinlock( ) : m_thread_id( ( OsThreadHandle )-1 ), m_value( 0 ), m_count( 0 ) { }
        ~OsSpinlock( ) { }

        void enter( )
        {
            if ( m_thread_id == OsThread::currentThreadId( ) )
            {
                m_count++;
                return;
            }

            while( !m_value.testAndSetOrdered ( 0, 1 ) )
            {
                QThread::yieldCurrentThread( );
            }

            if ( m_thread_id == ( OsThreadHandle )-1 )
            {
                m_thread_id = OsThread::currentThreadId( );
            }

            m_count++;
        }

        void leave( )
        {
            m_count--;
            if( m_count == 0 )
            {
                m_thread_id = ( OsThreadHandle )-1;
                m_value.fetchAndStoreOrdered( 0 );
            }
        }

        /**
         * @class Scoped
         * @brief Spinlock scoped lock/unlock
         *
         * @ingroup Platforms
         */
        class Scoped
        {
        public:
            Scoped( OsSpinlock* critical_section ): m_lock( critical_section ) { if ( m_lock ) m_lock->enter( ); }
            ~Scoped( ){ if ( m_lock ) m_lock->leave( ); }
        private:
            OsSpinlock* m_lock;
        };
    private:
        QAtomicInt m_value;
        OsThreadHandle m_thread_id;
        int m_count;
    };

    /**
     * @class OsSignal
     * @brief Signal in Qt
     *
     * @ingroup Platforms
     */
    class OsSignal: public QWaitCondition, public Object
    {
    public:
        OsSignal( ) : m_mutex( 0 ) { }
        bool wait( int timeout ) 
        {
            OsMutex::Scoped protect( &m_mutex );

            // True - wait period is finished, False - otherwise
            return !QWaitCondition::wait( &m_mutex, timeout ); 
        }
        void wakeup( ){ wakeOne( ); }
    private:
        OsMutex m_mutex;
    };

#if 0

    /**
     * OBSOLETE! Qt meta system is not used... any more
     * Invoke a meta function
     */
    #define INVOKE_ACTION( statemachine, action_index, event )  \
    { \
        void* ptr = event ? ( void* )event->data( ) : NULL; \
        void** pptr = ( void** )&ptr; \
        void* aptr[ 2 ] = { 0, pptr }; \
        statemachine->qt_metacall( QMetaObject::InvokeMetaMethod, action_index, aptr ); \
    }
#endif
}
#endif
